/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.filter;

import java.security.Principal;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.util.ExceptionResponse;
import com.cred.industries.platform.business.facade.CustomerFacade;
import com.cred.industries.platform.business.facade.SessionFacade;
import com.cred.industries.platform.business.objects.CustomerBO;
import com.cred.industries.platform.defines.Role;
import com.cred.industries.platform.session.SessionData;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;

/**
 * <p>We are authenticating based on a session key that is passed back to the user
 * They would first need to call createSession to get a session key
 * Then we use that session key to authenticate on every request </p>
 * By using the jersey ContainerRequestFilter, we can inject the 
 * security context that allows us to use the @rolesAllowed tag
 * 
 * The authenticated user is also stored into the session data, so we
 * can identify ourselves from anywhere in the program
 */
public class AuthenticationFilter implements ContainerRequestFilter {

	private static final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);
	
	private static final String SESSION_PARAMETER = "sessionid";

    /**
     * <p>The URI information for this request.</p>
     */
    @Context
    UriInfo mUriInfo;

    @Context 
    HttpServletRequest mReq;
    /**
     * called by jersey, we first authenticate the user then
     * we add the authorized user to the security context. 
     * If we can not authenticate the user we log them in 
     * as a guest. Guests have very few permissions
     */
    @Override
	public ContainerRequest filter(ContainerRequest cr) {
		
		CustomerBO customer = authenticate(cr);
		cr.setSecurityContext(new Authorizer(customer));
	    return cr;
	}
	
    /**
     * This has 2 paths. If we used http authentication, we grab the 
     * users name from the HttpServletRequest, Otherwise we 
     * gets the session key out of the CR and looks up a 
     * customer that matches the session key. 
     * If no customer is found we authenticate them as the guest
     * @param cr the request from the client
     * @return the client that is found to match the cr
     * @throws WebApplicationException, used by jersey to build a responce for the clients
     */
	private CustomerBO authenticate(ContainerRequest cr) {

		String user = null;
		if(mReq.getUserPrincipal() != null) {
			user = mReq.getUserPrincipal().getName();
		}
		
		if(user != null) {
			CustomerFacade custF = new CustomerFacade();
			CustomerBO cust = custF.getCustomerByPersona(user);
			
			if(cust == null) {
				cust = custF.getCustomerByPersona("guest");
			}
			
			//save the customer we found to match the session key into the
			//session data so we can get it later
			SessionData.setSessionCustomer(cust);
			return cust;
		} else {
			String sessionId = cr.getHeaderValue(SESSION_PARAMETER);
			
			try {
				SessionFacade session = new SessionFacade();
				CustomerBO cust = session.authenticateUserbySessionId(sessionId);
							
				//save the customer we found to match the session key into the
				//session data so we can get it later
				SessionData.setSessionCustomer(cust);
				return cust;
				
			} catch (ApplicationException e) {
				logger.error("Exception " + e.toString() + " " + e.getMessage());
				e.printStackTrace();
				throw new WebApplicationException(ExceptionResponse.build(Status.INTERNAL_SERVER_ERROR));
			} 
		}

	}

	/**
     * <p>SecurityContext used to perform authorization checks.</p>
     */
    public class Authorizer implements SecurityContext {

    	 private CustomerBO mCustomer;
        public Authorizer(final CustomerBO customer) {
        	mCustomer = customer;
        }

        public Principal getUserPrincipal() {
            return new Principal() {
                public String getName() {
                    return mCustomer.getRoles().getHighestRole().toString();
                }
            };
        }

        public boolean isUserInRole(String role) {
        	
            return mCustomer.getRoles().hasRole(Role.convert(role));
        }

        public boolean isSecure() {
            return "https".equals(mUriInfo.getRequestUri().getScheme());
        }

        public String getAuthenticationScheme() {
            return SecurityContext.BASIC_AUTH;
        }
    }

}
